package MoC;

import java.util.*;
import antlr.collections.AST;

import ptolemy.actor.*;

import db.*;
import SymbolTable.*;
import FlowGraph.*;
import FlowGraph.Nodes.*;
import util.*;
import MethodGraph.*;
import JavaGrammar.*;
import FlowGraph.SSA.*;

//////////////////////////////////////////////////////////////////////////
//// Restrictions
/**
        Checa se as seguintes restricoes :

        1. So loops contaveis
        2. Constantes nos parametros dos metodos

@author Ivan Jeukens
@version v 0.1 20/06/2003
*/
public class CommonMoCRestrictions {

    public CommonMoCRestrictions(ResultMessages msg) {
        _resultMessages = msg;
        _checkedClasses = new HashMap();
    }

    ///////////////////////////////////////////////////////////////////
    ////                        public methods                     ////

    public boolean check(ClassInfo cinfo) {
        boolean invalid = false;
        _currentClassInfo = cinfo;

        Iterator ite = cinfo.classesIterator();
        while(ite.hasNext()) {
            ClassData cdata = (ClassData) ite.next();
            if(_checkedClasses.containsKey(cdata)) {
                Boolean b = (Boolean) _checkedClasses.get(cdata);
                invalid = invalid || b.booleanValue();
                continue;
            }
            boolean classinvalid = false;
            Iterator mite = cdata.methodsIterator();
            while(mite.hasNext()) {
                LinkedList l = (LinkedList) mite.next();
                for(int i = 0;i < l.size();i++) {
                    STMethodEntry me = (STMethodEntry) l.get(i);
                    FlowGraph fg = me.getFlowGraph();
                    if(fg.isBuild()) {
                        MethodGraph mg = fg.getMethodGraph();
                        mg.checkContainsIOMethods();
                        Iterator nodesite = mg.getTopologicalOrder().iterator();
                        while(nodesite.hasNext()) {
                            MethodGraphNode mgnode = (MethodGraphNode) nodesite.next();
                            if(mgnode.getType() == MethodGraphNode.LOOPHEAD) {
                                if(!_countableLoops(mgnode, cdata)) {
                                    classinvalid = true;
                                }
                            }
                            mgnode.extractInfo();
                            if(!_checkNodeRestrictions(mgnode, cdata)) {
                                classinvalid = true;
                            }
                        }
                    }
                }
            }
            _checkedClasses.put(cdata, new Boolean(classinvalid));
            invalid = invalid || classinvalid;
        }
        return invalid;
    }

    ///////////////////////////////////////////////////////////////////
    ////                        private methods                    ////

    private boolean _checkNodeRestrictions(MethodGraphNode node,
            ClassData cinfo) {
        boolean ret = true;
        int channel = node.getChannel();
        if(channel == MethodGraphNode.UNDEFCHANNEL) {
            MethodCallNode mcnode = node.getMethodCallNode();
            LinkedList lines = new LinkedList();
            lines.add(mcnode.getLine());
            _newMessage(Message.ERROR, Message.INVALID_CHANNELINDEX,
               "Invalid channelindex parameter.", lines);
            ret = false;
        }

        // Verifica se tem nested loop dentro de um getWidth
        if(node.getType() == MethodGraphNode.LOOPHEAD) {
            MethodGraphNode head = node.getInsideLoopBody();
            if(head != null) {
                FlowGraphNode fgn = head.getLoopHead();
                if(fgn.getType() == FlowGraphNode.FOR_BEGIN) {
                    ForBeginNode fbeg = (ForBeginNode) fgn;
                    if(fbeg.isGetWidthLoop()) {
                        LinkedList lines = new LinkedList();
                        lines.add(fbeg.getConditionLine());
                        lines.add(fbeg.getEndLine());
                        _newMessage(Message.ERROR, Message.LOOP_INSIDE_GETWIDTH,
                           "Loop nested inside a getWidth() loop", lines);
                       ret = false;
                   }
               }
            }
        }

        int ntokens = node.getNTokens();
        if(ntokens == -1) {
            MethodCallNode mcnode = node.getMethodCallNode();
            LinkedList lines = new LinkedList();
            lines.add(mcnode.getLine());
            _newMessage(Message.ERROR, Message.INVALID_VECTORLENGTH,
               "Invalid vectorlength parameter.", lines);
           ret = false;
        }

        return ret;
    }

    private boolean _countableLoops(MethodGraphNode mgnode, ClassData cinfo) {
        boolean ret = true;
        // Verifica se tem algum break ou continue ou return ou throw
        FlowGraphNode fgnode = mgnode.getLoopHead();

        if(!mgnode.containsIOMethods()) return true;

        if(_searchLoopExit(fgnode, cinfo)) {
            ret = false;
        }

        // Verifica se o loop e contavel
        if(fgnode.getType() == FlowGraphNode.FOR_BEGIN) {
            ForBeginNode fbeg = (ForBeginNode) fgnode;
            if(!_checkForLoop(fbeg, mgnode, cinfo)) {
                LinkedList lines = new LinkedList();
                lines.add(fbeg.getConditionLine());
                lines.add(fbeg.getEndLine());
                _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                   "Not a countable loop.", lines);
                ret = false;
            }
        }
        else
        if(fgnode.getType() == FlowGraphNode.WHILE_BEGIN) {
            WhileBeginNode wbeg = (WhileBeginNode) fgnode;
            if(!_checkWhileLoop(wbeg, cinfo)) {
                LinkedList lines = new LinkedList();
                lines.add(wbeg.getConditionLine());
                lines.add(wbeg.getEndLine());
                _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                   "Not a countable loop.", lines);
                ret = false;
            }
        }
        else
        if(fgnode.getType() == FlowGraphNode.DO_BEGIN) {
            DoBeginNode dbeg = (DoBeginNode) fgnode;
            if(!_checkDoLoop(dbeg, cinfo)) {
                LinkedList lines = new LinkedList();
                lines.add(dbeg.getBegLine());
                lines.add(dbeg.getConditionLine());
                _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                   "Not a countable loop.", lines);
                ret = false;
            }
        }
        return ret;
    }

    private boolean _checkDoLoop(DoBeginNode dbeg, ClassData cinfo) {
        AST ast = dbeg.getCondition();
        if(ast == null) {
            // Nao tem condicao.
            return false;
        }
        AST cmpast = ast.getFirstChild();
        
        if(!_validCmpOperator(cmpast)) {
            LinkedList lines = new LinkedList();
            lines.add(dbeg.getConditionLine());
            _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
               "Not a countable loop. Invalid compare operator.", lines);
            return false;
        }

        int cte;
        String var;
        // tem que encontrar um ID e um cte
        AST child = cmpast.getFirstChild();
        if(child.getType() == JavaTokenTypes.NUM_INT) {
            cte = (new Integer(child.getText())).intValue();
            child = child.getNextSibling();
            if(child.getType() == JavaTokenTypes.IDENT) {
                var = child.getText();
            }
            else {
                LinkedList lines = new LinkedList();
                lines.add(dbeg.getConditionLine());
                _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                   "Not a countable loop. One operand not variable.", lines);
                return false;
            }
        }
        else
        if(child.getType() == JavaTokenTypes.IDENT) {
            var = child.getText();
            child = child.getNextSibling();
            if(child.getType() == JavaTokenTypes.NUM_INT) {
                cte = (new Integer(child.getText())).intValue();
            }
            else {
                LinkedList lines = new LinkedList();
                lines.add(dbeg.getConditionLine());
                _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                   "Not a countable loop. One operand not constant integer", 
                   lines);
                return true;
            }
        }
        else {
            LinkedList lines = new LinkedList();
            lines.add(dbeg.getConditionLine());
            _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
               "Not a countable loop. Invalid compare function.", lines);
            return false;
        }
        ast = cmpast.getNextSibling();
        if(ast != null) return false;   // nao pode ter nada depois

        var = SSA.getVarName(var);

        // Ja tenho a variavel do loop. Procura nos phi nodes da loophead,
        // pela inicializacao.
        int initValue = -1;
        boolean found = false;
        Iterator ite = dbeg.phiNodeIterator();
        BasicBlockNode bb = null;       // O no onde tem o statement de incremento
        while(ite.hasNext()) {
            PhiNode phi = (PhiNode) ite.next();
            AST deflabel = phi.getDefLabel();
            String label = SSA.getVarName(deflabel.getText());
            if(label.equals(var)) {
                VariableInfo vinfo = phi.getVariableInfo();
                for(int i = 0;i < phi.inputSize();i++) {
                    AST lab = phi.getLabel(i);
                    if(lab.getType() == JavaTokenTypes.NUM_INT) {
                        initValue = (new Integer(lab.getText())).intValue();
                    }
                    else
                    if(SSA.getVarName(lab.getText()).equals(var)) {
                        int index = SSA.getVarIndex(lab.getText());
                        LinkedList defs = vinfo.getDefFromChain(index);
                        for(int k = 0;k < defs.size();k++) {
                            VariableLocation loc = (VariableLocation)
                                defs.get(k);
                            bb = (BasicBlockNode) loc.getFGNode();
                        }
                    }
                }
            }
            if(initValue != -1) break;
        }
        if(initValue == -1) return false;

        int incop = -1;
        int incr = -1;

        for(int i = 0;i < bb.statementsSize();i++) {
            AST stat = bb.getStatement(i);
            if(stat.getType() == JavaTokenTypes.EXPR) {
                AST op1ast = stat.getFirstChild();
                switch(op1ast.getType()) {

                    case JavaTokenTypes.POST_DEC:
                    case JavaTokenTypes.DEC:
                    case JavaTokenTypes.POST_INC:
                    case JavaTokenTypes.INC: {
                        child = op1ast.getFirstChild();
                        String cl = SSA.getVarName(child.getText());
                        if(cl.equals(var)) {
                            incop = op1ast.getType();
                            incr = 1;
                            int rep = _computeTotalRepetitions(initValue,
                                cmpast.getType(), cte, incr);

                            dbeg.setIterations(rep);
                            return true;
                        }
                    } break;

                    case JavaTokenTypes.PLUS_ASSIGN:
                    case JavaTokenTypes.MINUS_ASSIGN: {
                        child = op1ast.getFirstChild();
                        String cl = SSA.getVarName(child.getText());
                        if(cl.equals(var)) {
                            child = child.getNextSibling();
                            if(child.getType() == JavaTokenTypes.NUM_INT) {
                                incop = op1ast.getType();
                                incr = (new Integer(child.getText())).intValue();
                                int rep = _computeTotalRepetitions(initValue,
                                    cmpast.getType(), cte, incr);

                                dbeg.setIterations(rep);
                                break;
                            }
                        }
                    } break;

                    case JavaTokenTypes.ASSIGN: {
                        child = op1ast.getFirstChild();
                        String cl = SSA.getVarName(child.getText());
                        if(cl.equals(var)) {
                            AST opast = child.getNextSibling();
                            switch(opast.getType()) {
                                case JavaTokenTypes.MINUS:
                                case JavaTokenTypes.PLUS: {
                                    child = opast.getFirstChild();
                                    cl = SSA.getVarName(child.getText());
                                    if(child.getType() == JavaTokenTypes.IDENT &&
                                       cl.equals(var)) {
                                        child = child.getNextSibling();
                                        if(child.getType() == JavaTokenTypes.NUM_INT) {
                                            incr = (new Integer(child.getText())).intValue();
                                            int rep = _computeTotalRepetitions(
                                                initValue,
                                                cmpast.getType(),
                                                cte, incr);
                                            break;
                                        }
                                    }
                                    else
                                    if(child.getType() == JavaTokenTypes.NUM_INT) {
                                        child = child.getNextSibling();
                                        cl = SSA.getVarName(child.getText());
                                        if(child.getType() == JavaTokenTypes.IDENT &&
                                           cl.equals(var)) {
                                            incr = (new Integer(child.getText())).intValue();
                                            int rep = _computeTotalRepetitions(
                                                initValue,
                                                cmpast.getType(),
                                                cte, incr);
                                            break;
                                        }
                                    }
                                    
                                } break;
                            }
                        }
                    } break;

                }
            }
        }
        return false;
    }

    /* Verifica se esse loop for e contavel. Primeiro olha a condicao,
    para determinar qual e a variavel, e o limite. Depois a expressao de
    iteracao, e ao final, o valor inicial. */
    private boolean _checkForLoop(ForBeginNode fbeg, MethodGraphNode mgnode, 
            ClassData cinfo) {
        AST ast = fbeg.getCondition();
        ast = ast.getFirstChild();
        if(ast == null) {
            // Nao tem condicao.
            return false;
        }
        AST cmpast = ast.getFirstChild();

        if(!_validCmpOperator(cmpast)) {
            LinkedList lines = new LinkedList();
            lines.add(fbeg.getConditionLine());
            _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
               "Not a countable loop. Invalid compare operator.", lines);
            return false;
        }

        int cte = -1;
        String var = null;
        boolean getwidth = false;
        // tem que encontrar um ID e um cte
        AST child = cmpast.getFirstChild();

        if(child.getType() == JavaTokenTypes.NUM_INT) {
            cte = (new Integer(child.getText())).intValue();
            child = child.getNextSibling();
            if(child.getType() == JavaTokenTypes.IDENT) {
                var = child.getText();
            }
            else {
                LinkedList lines = new LinkedList();
                lines.add(fbeg.getConditionLine());
                _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                   "Not a countable loop. One operand not variable.", lines);
                return false;
            }
        }
        else
        if(child.getType() == JavaTokenTypes.IDENT) {
            var = child.getText();
            child = child.getNextSibling();
            if(child.getType() == JavaTokenTypes.NUM_INT) {
                cte = (new Integer(child.getText())).intValue();
            }
            else {
                 // verifica se e um getWidth. Para isso procura na definicao
                 // anterior a este id (getWidth e um metodo que foi extraido)
                if(child.getType() == JavaTokenTypes.IDENT) {
                    String vcmp = child.getText();
                    STEntry entry = fbeg.getVar(SSA.getVarName(vcmp));
                    if(entry != null) {
                        FlowGraph fg = fbeg.getContainer();
                        SSA ssa = fg.getSSA();
                        VariableInfo varinfo = ssa.getVarInfo(entry);
                        int index = SSA.getVarIndex(vcmp);
                        LinkedList l = varinfo.getDefFromChain(index - 1);
                        if(l == null || l.size() == 0) {
                            LinkedList lines = new LinkedList();
                            lines.add(fbeg.getConditionLine());
                            _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                               "Not a countable loop. One operand not constant integer", 
                               lines);
                            return false;
                        }
                        VariableLocation loc = (VariableLocation) l.get(0);
                        FlowGraphNode fgnode = loc.getFGNode();
                        if(fgnode.getType() == FlowGraphNode.METHOD_CALL) {
                            MethodCallNode mcnode = (MethodCallNode) fgnode;
                            if(mcnode.getMethodName().equals("getWidth") &&
                               Util.isSubClass(mcnode.getCallerType(), IOPort.class)) {
                                fbeg.setGetWidthLoop(var);
                                getwidth = true;
                            }
                            else {
                                LinkedList lines = new LinkedList();
                                lines.add(fbeg.getConditionLine());
                                _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                                   "Not a countable loop. One operand not constant integer", 
                                   lines);
                                return false;
                            }
                        }
                        else {
                            LinkedList lines = new LinkedList();
                            lines.add(fbeg.getConditionLine());
                            _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                               "Not a countable loop. One operand not constant integer", 
                               lines);
                            return false;
                        }
                    }
                    else {
                        LinkedList lines = new LinkedList();
                        lines.add(fbeg.getConditionLine());
                        _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                           "Not a countable loop. One operand not constant integer", 
                           lines);
                        return false;
                    }
                }
                else {
                    LinkedList lines = new LinkedList();
                    lines.add(fbeg.getConditionLine());
                    _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                       "Not a countable loop. One operand not constant integer", 
                       lines);
                    return false;
                }
            }
        }
        else {
            LinkedList lines = new LinkedList();
            lines.add(fbeg.getConditionLine());
            _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
               "Not a countable loop. Invalid compare function.",
               lines);
            return false;
        }
        ast = cmpast.getNextSibling();
        if(ast != null) return false;   // nao pode ter nada depois

        var = SSA.getVarName(var);

        // Ja tenho a variavel do loop. Procura nos phi nodes da loophead,
        // pela inicializacao.
        int initValue = -1;
        boolean found = false;
        Iterator ite = fbeg.phiNodeIterator();
        while(ite.hasNext()) {
            PhiNode phi = (PhiNode) ite.next();
            AST deflabel = phi.getDefLabel();
            String label = SSA.getVarName(deflabel.getText());
            if(label.equals(var)) {
                for(int i = 0;i < phi.inputSize() - 1;i++) {
                    AST lab = phi.getLabel(i);
                    if(lab.getType() == JavaTokenTypes.NUM_INT) {
                        initValue = (new Integer(lab.getText())).intValue();
                        break;
                    }
                }
            }
            if(initValue != -1) break;
        }
        if(initValue == -1) return false;

        if(getwidth && (initValue != 0)) {
            LinkedList lines = new LinkedList();
            lines.add(fbeg.getConditionLine());
            _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
               "Loop with IOPort.getWidth() condition. Must start from 0", 
               lines);
            return false;
        }

        int incop = -1;
        int incr = -1;

        FlowGraphNode iteex = null;
        for(int i = 0;i < fbeg.iteExtractedNodeSize();i++) {
            iteex = (FlowGraphNode) fbeg.getIteExtractedNode(i);
            if(iteex.getType() == FlowGraphNode.BASIC_BLOCK) break;
        }
        BasicBlockNode bb = (BasicBlockNode) iteex;
        for(int i = 0;i < bb.statementsSize();i++) {
            AST stat = bb.getStatement(i);
            if(stat.getType() == JavaTokenTypes.EXPR) {
                AST op1ast = stat.getFirstChild();
                switch(op1ast.getType()) {

                    case JavaTokenTypes.POST_DEC:
                    case JavaTokenTypes.DEC:
                    case JavaTokenTypes.POST_INC:
                    case JavaTokenTypes.INC: {
                        child = op1ast.getFirstChild();
                        String cl = SSA.getVarName(child.getText());
                        if(cl.equals(var)) {
                            incop = op1ast.getType();
                            if(getwidth && incop != JavaTokenTypes.POST_INC) {
                                LinkedList lines = new LinkedList();
                                lines.add(fbeg.getConditionLine());
                                _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                                   "Loop with IOPort.getWidth(). Must have a increment = 1", 
                                   lines);
                                return false;
                            }
                            else
                            if(getwidth) {
                                fbeg.setIterations(1);
                                return true;
                            }

                            incr = 1;
                            int rep = _computeTotalRepetitions(initValue,
                                cmpast.getType(), cte, incr);

                            fbeg.setIterations(rep);
                            return true;
                        }
                    } break;

                    case JavaTokenTypes.PLUS_ASSIGN:
                    case JavaTokenTypes.MINUS_ASSIGN: {
                        child = op1ast.getFirstChild();
                        String cl = SSA.getVarName(child.getText());
                        if(cl.equals(var)) {
                            child = child.getNextSibling();
                            if(child.getType() == JavaTokenTypes.NUM_INT) {
                                incop = op1ast.getType();
                                incr = (new Integer(child.getText())).intValue();

                                if(getwidth && incop != JavaTokenTypes.PLUS_ASSIGN) {
                                    LinkedList lines = new LinkedList();
                                    lines.add(fbeg.getConditionLine());
                                    _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                                       "Loop with IOPort.getWidth(). Must have a increment = 1", 
                                       lines);
                                    return false;
                                }
                                else
                                if(getwidth && incr != 1) {
                                    LinkedList lines = new LinkedList();
                                    lines.add(fbeg.getConditionLine());
                                    _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                                       "Loop with IOPort.getWidth(). Must have a increment = 1", 
                                       lines);
                                    return false;
                                }
                                else
                                if(getwidth) {
                                    fbeg.setIterations(1);
                                    return true;
                                }

                                int rep = _computeTotalRepetitions(initValue,
                                    cmpast.getType(), cte, incr);

                                fbeg.setIterations(rep);
                                break;
                            }
                        }
                    } break;

                    case JavaTokenTypes.ASSIGN: {
                        child = op1ast.getFirstChild();
                        String cl = SSA.getVarName(child.getText());
                        if(cl.equals(var)) {
                            AST opast = child.getNextSibling();
                            switch(opast.getType()) {
                                case JavaTokenTypes.MINUS:
                                case JavaTokenTypes.PLUS: {
                                    child = opast.getFirstChild();
                                    cl = SSA.getVarName(child.getText());
                                    if(child.getType() == JavaTokenTypes.IDENT &&
                                       cl.equals(var)) {
                                        child = child.getNextSibling();
                                        if(child.getType() == JavaTokenTypes.NUM_INT) {
                                            incr = (new Integer(child.getText())).intValue();

                                            if(getwidth && opast.getType() != JavaTokenTypes.PLUS) {
                                                LinkedList lines = new LinkedList();
                                                lines.add(fbeg.getConditionLine());
                                                _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                                                   "Loop with IOPort.getWidth(). Must have a increment = 1", 
                                                   lines);
                                                return false;
                                            }
                                            else
                                            if(getwidth && incr != 1) {
                                                LinkedList lines = new LinkedList();
                                                lines.add(fbeg.getConditionLine());
                                                _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                                                   "Loop with IOPort.getWidth(). Must have a increment = 1", 
                                                   lines);
                                                return false;
                                            }
                                            else
                                            if(getwidth) {
                                                fbeg.setIterations(1);
                                                return true;
                                            }

                                            int rep = _computeTotalRepetitions(
                                                initValue,
                                                cmpast.getType(),
                                                cte, incr);
                                            break;
                                        }
                                    }
                                    else
                                    if(child.getType() == JavaTokenTypes.NUM_INT) {
                                        child = child.getNextSibling();
                                        cl = SSA.getVarName(child.getText());
                                        if(child.getType() == JavaTokenTypes.IDENT &&
                                           cl.equals(var)) {

                                            incr = (new Integer(child.getText())).intValue();

                                            if(getwidth && opast.getType() != JavaTokenTypes.PLUS) {
                                                LinkedList lines = new LinkedList();
                                                lines.add(fbeg.getConditionLine());
                                                _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                                                   "Loop with IOPort.getWidth(). Must have a increment = 1", 
                                                   lines);
                                                return false;
                                            }
                                            else
                                            if(getwidth && incr != 1) {
                                                LinkedList lines = new LinkedList();
                                                lines.add(fbeg.getConditionLine());
                                                _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                                                   "Loop with IOPort.getWidth(). Must have a increment = 1", 
                                                   lines);
                                                return false;
                                            }
                                            else
                                            if(getwidth) {
                                                fbeg.setIterations(1);
                                                return true;
                                            }
                                            
                                            int rep = _computeTotalRepetitions(
                                                initValue,
                                                cmpast.getType(),
                                                cte, incr);
                                            break;
                                        }
                                    }
                                } break;
                            }
                        }
                    } break;
                }
            }
        }
        return true;
    }

    private boolean _checkWhileLoop(WhileBeginNode wbeg, ClassData cinfo) {
        AST ast = wbeg.getCondition();
        if(ast == null) {
            // Nao tem condicao.
            return false;
        }
        AST cmpast = ast.getFirstChild();

        if(!_validCmpOperator(cmpast)) {
            LinkedList lines = new LinkedList();
            lines.add(wbeg.getConditionLine());
            _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
               "Not a countable loop. Invalid compare operator.", 
               lines);
            return false;
        }

        int cte;
        String var;
        // tem que encontrar um ID e um cte
        AST child = cmpast.getFirstChild();
        if(child.getType() == JavaTokenTypes.NUM_INT) {
            cte = (new Integer(child.getText())).intValue();
            child = child.getNextSibling();
            if(child.getType() == JavaTokenTypes.IDENT) {
                var = child.getText();
            }
            else {
                LinkedList lines = new LinkedList();
                lines.add(wbeg.getConditionLine());
                _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                   "Not a countable loop. One operand not variable.", 
                   lines);
                return false;
            }
        }
        else
        if(child.getType() == JavaTokenTypes.IDENT) {
            var = child.getText();
            child = child.getNextSibling();
            if(child.getType() == JavaTokenTypes.NUM_INT) {
                cte = (new Integer(child.getText())).intValue();
            }
            else {
                LinkedList lines = new LinkedList();
                lines.add(wbeg.getConditionLine());
                _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
                   "Not a countable loop. One operand not constant integer", 
                   lines);
                return true;
            }
        }
        else {
            LinkedList lines = new LinkedList();
            lines.add(wbeg.getConditionLine());
            _newMessage(Message.ERROR, Message.LOOP_NOT_COUNTABLE,
               "Not a countable loop. Invalid compare function.",
               lines);
            return false;
        }
        ast = cmpast.getNextSibling();
        if(ast != null) return false;   // nao pode ter nada depois

        var = SSA.getVarName(var);

        // Ja tenho a variavel do loop. Procura nos phi nodes da loophead,
        // pela inicializacao.
        int initValue = -1;
        boolean found = false;
        Iterator ite = wbeg.phiNodeIterator();
        BasicBlockNode bb = null;       // O no onde tem o statement de incremento
        while(ite.hasNext()) {
            PhiNode phi = (PhiNode) ite.next();
            AST deflabel = phi.getDefLabel();
            String label = SSA.getVarName(deflabel.getText());
            if(label.equals(var)) {
                VariableInfo vinfo = phi.getVariableInfo();
                for(int i = 0;i < phi.inputSize();i++) {
                    AST lab = phi.getLabel(i);
                    if(lab.getType() == JavaTokenTypes.NUM_INT) {
                        initValue = (new Integer(lab.getText())).intValue();
                    }
                    else
                    if(SSA.getVarName(lab.getText()).equals(var)) {
                        int index = SSA.getVarIndex(lab.getText());
                        LinkedList defs = vinfo.getDefFromChain(index);
                        for(int k = 0;k < defs.size();k++) {
                            VariableLocation loc = (VariableLocation)
                                defs.get(k);
                            bb = (BasicBlockNode) loc.getFGNode();
                        }
                    }
                }
            }
            if(initValue != -1) break;
        }
        if(initValue == -1) return false;

        int incop = -1;
        int incr = -1;

        for(int i = 0;i < bb.statementsSize();i++) {
            AST stat = bb.getStatement(i);
            if(stat.getType() == JavaTokenTypes.EXPR) {
                AST op1ast = stat.getFirstChild();
                switch(op1ast.getType()) {

                    case JavaTokenTypes.POST_DEC:
                    case JavaTokenTypes.DEC:
                    case JavaTokenTypes.POST_INC:
                    case JavaTokenTypes.INC: {
                        child = op1ast.getFirstChild();
                        String cl = SSA.getVarName(child.getText());
                        if(cl.equals(var)) {
                            incop = op1ast.getType();
                            incr = 1;
                            int rep = _computeTotalRepetitions(initValue,
                                cmpast.getType(), cte, incr);

                            wbeg.setIterations(rep);
                            return true;
                        }
                    } break;

                    case JavaTokenTypes.PLUS_ASSIGN:
                    case JavaTokenTypes.MINUS_ASSIGN: {
                        child = op1ast.getFirstChild();
                        String cl = SSA.getVarName(child.getText());
                        if(cl.equals(var)) {
                            child = child.getNextSibling();
                            if(child.getType() == JavaTokenTypes.NUM_INT) {
                                incop = op1ast.getType();
                                incr = (new Integer(child.getText())).intValue();
                                int rep = _computeTotalRepetitions(initValue,
                                    cmpast.getType(), cte, incr);

                                wbeg.setIterations(rep);
                                break;
                            }
                        }
                    } break;

                    case JavaTokenTypes.ASSIGN: {
                        child = op1ast.getFirstChild();
                        String cl = SSA.getVarName(child.getText());
                        if(cl.equals(var)) {
                            AST opast = child.getNextSibling();
                            switch(opast.getType()) {
                                case JavaTokenTypes.MINUS:
                                case JavaTokenTypes.PLUS: {
                                    child = opast.getFirstChild();
                                    cl = SSA.getVarName(child.getText());
                                    if(child.getType() == JavaTokenTypes.IDENT &&
                                       cl.equals(var)) {
                                        child = child.getNextSibling();
                                        if(child.getType() == JavaTokenTypes.NUM_INT) {
                                            incr = (new Integer(child.getText())).intValue();
                                            int rep = _computeTotalRepetitions(
                                                initValue,
                                                cmpast.getType(),
                                                cte, incr);
                                            break;
                                        }
                                    }
                                    else
                                    if(child.getType() == JavaTokenTypes.NUM_INT) {
                                        child = child.getNextSibling();
                                        cl = SSA.getVarName(child.getText());
                                        if(child.getType() == JavaTokenTypes.IDENT &&
                                           cl.equals(var)) {
                                            incr = (new Integer(child.getText())).intValue();
                                            int rep = _computeTotalRepetitions(
                                                initValue,
                                                cmpast.getType(),
                                                cte, incr);
                                            break;
                                        }
                                    }
                                    
                                } break;
                            }
                        }
                    } break;

                }
            }
        }
        
        return false;
    }

    private int _computeTotalRepetitions(int initvalue, int cmpfunc, 
            int cmpvalue, int incvalue) {
        int rep = 0;
        if(cmpfunc == JavaTokenTypes.LE ||
           cmpfunc == JavaTokenTypes.GE) {
            rep = Math.abs(cmpvalue - initvalue) + 1;
        }
        else {
            rep = Math.abs(cmpvalue - initvalue);
        }
        rep = rep / incvalue;

        return rep;
    }

    // Verifica se e um operado de comparacao
    private boolean _validCmpOperator(AST ast) {
        if(ast == null) return false;
        if(ast.getType() == JavaTokenTypes.GT ||
           ast.getType() == JavaTokenTypes.GE ||
           ast.getType() == JavaTokenTypes.LT ||
           ast.getType() == JavaTokenTypes.LE) {
            return true;
        }
        return false;
    }

    /* Procura nesse loop por algum no de saida. O parametro e o loop head */
    private boolean _searchLoopExit(FlowGraphNode node, ClassData cinfo) {
        boolean contains = false;
        LinkedList process = new LinkedList();

        Iterator ite = node.outputEdgesIterator();
        while(ite.hasNext()) {
            FlowGraphEdge edge = (FlowGraphEdge) ite.next();
            if(edge.isBack() || edge.isDeleted()) continue;

            FlowGraphNode n = edge.getTo();

            if(n instanceof LoopEndNode) continue;
            process.add(node);
        }

        HashMap processed = new HashMap();
        while(process.size() > 0) {
            FlowGraphNode n = (FlowGraphNode) process.remove(0);
            processed.put(n, null);
            
            if(n.getType() == FlowGraphNode.BREAK ||
               n.getType() == FlowGraphNode.CONTINUE ||
               n.getType() == FlowGraphNode.THROW ||
               n.getType() == FlowGraphNode.RETURN) {
                LinkedList lines = new LinkedList();
                lines.add(n.getLine());
                _newMessage(Message.ERROR, Message.LOOP_WITH_EXIT,
                   "Not a countable loop. Contains an exit path", 
                   lines);
                contains = true;

                continue;
            }

            ite = n.outputEdgesIterator();
            while(ite.hasNext()) {
                FlowGraphEdge edge = (FlowGraphEdge) ite.next();
                if(edge.isBack() || edge.isDeleted()) continue;

                if(edge.isException()) {
                    LinkedList lines = new LinkedList();
                    lines.add(n.getLine());
                    _newMessage(Message.ERROR, Message.LOOP_WITH_EXIT,
                       "Not a countable loop. Contains an exit path", 
                       lines);
                    contains = true;

                    continue;
                }
                FlowGraphNode outn = edge.getTo();

                //if(outn.getType() == FlowGraphNode.DO_END) continue;
                if(outn instanceof LoopEndNode) continue;
                if(processed.containsKey(outn)) continue;
                process.add(outn);
            }
        }

        return contains;
    }

    private Message _newMessage(int status, int code, String text,
            LinkedList lines) {
        Message m = new Message(_currentClassInfo.getName(), Message.MOCVALIDATION,
            Message.NOMOC, status, code, text);
        ClassMessageLocation er = new ClassMessageLocation(
            MessageLocation.CLASS_SOURCE);
        for(int i = 0;i < lines.size();i++) {
            er.addLine((Line)lines.get(i));
        }
        er.setClassInfo(_currentClassInfo);
        m.setLocation(er);
        _resultMessages.addMessage(m);

        return m;
    }

    ///////////////////////////////////////////////////////////////////
    ////                        private variables                  ////

    private ResultMessages _resultMessages;
    private HashMap _checkedClasses;
    private ClassInfo _currentClassInfo;
}
